use std::fmt::{Debug, Formatter};
use std::ops::{Add, Div, Index, IndexMut, Mul, Sub};

pub struct Items<T>(pub Vec<T>);

pub struct ItemsIter<'a, T: Clone> {
    items: &'a Items<T>,
    index: usize,
}

impl<T: Add + Clone> Add for Items<T>
where
    Vec<T>: FromIterator<<T as Add>::Output>,
{
    type Output = Self;

    fn add(self, rhs: Self) -> Self {
        assert_eq!(self.0.len(), rhs.0.len());

        let data = self
            .0
            .iter()
            .zip(rhs.0.iter())
            .map(|(x, y)| x.clone() + y.clone())
            .collect();

        Items(data)
    }
}

impl<T: Sub + Clone> Sub for Items<T>
where
    Vec<T>: FromIterator<<T as Sub>::Output>,
{
    type Output = Items<T>;

    fn sub(self, rhs: Self) -> Self {
        assert_eq!(self.0.len(), rhs.0.len());

        let data = self
            .0
            .iter()
            .zip(rhs.0.iter())
            .map(|(x, y)| x.clone() - y.clone())
            .collect();

        Items(data)
    }
}

impl<T: Mul<T> + Clone> Mul<T> for Items<T>
where
    Vec<T>: FromIterator<<T as Mul<T>>::Output>,
{
    type Output = Items<T>;

    fn mul(self, rhs: T) -> Self::Output {
        let data = self.0.iter().map(|val| val.clone() * rhs.clone()).collect();

        Items(data)
    }
}

impl<T: Clone + Mul<f64, Output = T>> Mul<Items<T>> for f64 {
    type Output = Items<T>;

    fn mul(self, rhs: Items<T>) -> Items<T> {
        let data = rhs.0.iter().map(|val| val.clone() * self).collect();

        Items(data)
    }
}

impl<T: Div + Clone> Div<T> for Items<T>
where
    Vec<T>: FromIterator<<T as Div<T>>::Output>,
{
    type Output = Items<T>;

    fn div(self, rhs: T) -> Self::Output {
        let data = self.0.iter().map(|val| val.clone() / rhs.clone()).collect();

        Items(data)
    }
}

impl<'a, T: Clone> Iterator for ItemsIter<'a, T> {
    type Item = &'a T;

    fn next(&mut self) -> Option<Self::Item> {
        if self.index < self.items.0.len() {
            let item = &self.items.0[self.index];
            self.index += 1;
            Some(item)
        } else {
            None
        }
    }
}

impl<'a, T: Clone> ExactSizeIterator for ItemsIter<'a, T> {
    fn len(&self) -> usize {
        self.items.0.len()
    }
}

impl<T: Clone> Clone for Items<T> {
    fn clone(&self) -> Self {
        Self(self.0.clone())
    }
}

impl<T: Clone> Index<usize> for Items<T> {
    type Output = T;

    fn index(&self, index: usize) -> &Self::Output {
        &self.0[index]
    }
}

impl<T: Clone> IndexMut<usize> for Items<T> {
    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
        &mut self.0[index]
    }
}

impl<T: Clone> Items<T> {
    pub fn iter(&self) -> ItemsIter<T> {
        ItemsIter {
            items: self,
            index: 0,
        }
    }

    pub fn len(&self) -> usize {
        self.iter().len()
    }
}

impl<T: Debug + Clone> Debug for Items<T> {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        for col in 0..self.len() {
            if col != 0 {
                write!(f, "\t")?;
            }

            let data = self[col].clone();
            write!(f, "{:?}", data)?;
        }
        Ok(())
    }
}
